﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Media.Imaging;
using Odyssey.Behaviors;
using Odyssey.Controls.Primitives;
using Microsoft.Phone.Controls;
using Odyssey.Utils;
using System.Linq;

namespace Odyssey.Controls.Models
{
    /// <summary>
    /// Logic for opening and closing ContextMenus.
    /// </summary>
    public class ContextMenuViewModel
    {
        /// <summary>
        /// Creates a new  ContextMenuViewModel instance.
        /// </summary>
        /// <param name="element">Element that invokes the context menu.</param>
        /// <param name="menuTemplate">DataTemplate of the ContextMenu to open.</param>
        internal ContextMenuViewModel(FrameworkElement element, DataTemplate menuTemplate)
            : base()
        {
            IsPressed = true;
            Current = this;

            Control = element.GetVisualAncestorsAndSelf().OfType<Control>().First();
            if (Control.IsEnabled)
            {
                this.Element = element;
                this.MenuTemplate = menuTemplate;

                Element.ManipulationDelta += new EventHandler<ManipulationDeltaEventArgs>(HandleManipulationDelta);
                Element.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(HandleManipulationCompleted);
                // Element.Unloaded += new RoutedEventHandler(HandleUnloaded);

            }
        }


        void HandleUnloaded(object sender, RoutedEventArgs e)
        {
            if (Overlay != null) Overlay.IsOpen = false;
            CancelMenuOpening();
            UnbindElement();
        }


        /// <summary>
        /// Starts the delay timer to expire after opening the context menu.
        /// </summary>
        public void StartTimer()
        {
            timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromSeconds(0.6)
            };
            timer.Tick += new EventHandler(OnTick);
            timer.Start();
        }


        /// <summary>
        /// Gets the current active instance. As only one context menu can appear on time, this is a singleton property.
        /// </summary>
        public static ContextMenuViewModel Current { get; private set; }

        private DispatcherTimer timer;
        private bool isOpened;

        /// <summary>
        /// Gets whether the Element which invoked the context menu is still pressed.
        /// </summary>
        public bool IsPressed { get; private set; }

        /// <summary>
        /// Element that invoked the context menu.
        /// </summary>
        public FrameworkElement Element { get; private set; }

        /// <summary>
        /// Control to which the element belongs which invoked the context menu.
        /// </summary>
        public Control Control { get; private set; }

        /// <summary>
        /// DataTemplate that contains the layout of the context menu including it's items.
        /// </summary>
        public DataTemplate MenuTemplate { get; private set; }

        /// <summary>
        /// Overlay which hosts the context menu while opened.
        /// </summary>
        protected ContextMenuOverlay Overlay { get; private set; }


        void OnTick(object sender, EventArgs e)
        {
            if (timer != null)
            {
                timer.Tick -= OnTick;
                OpenMenu();
            }
        }

        /// <summary>
        /// Opens the context menu.
        /// </summary>
        public void OpenMenu()
        {
            if (!isOpened)
            {
                StopTimer();
                TiltEffect.StopTiltReturnStoryboardAndCleanup();
                isOpened = true;

                this.Control.Focus();
                WriteableBitmap itemImage = new WriteableBitmap(this.Control, null);
                Point offset = DetermineOffset();

                Overlay = new ContextMenuOverlay(this, Element)
                {
                    ItemImage = itemImage,
                    VerticalOffset = offset.Y,
                    HorizontalOffset = offset.X,
                    IsAbove = DetermineAbove(offset.Y),
                    Mode = OverlayMode.Replace,
                    ContentTemplate = MenuTemplate,
                    Content = Element.DataContext,

                };
                Overlay.Closing += delegate
                {
                    ContextMenuViewModel.Current = null;
                };

                Overlay.IsOpen = true;
            }
        }

        private void StopTimer()
        {
            if (timer != null)
            {
                timer.Stop();
                timer = null;
            }
        }


        private bool DetermineAbove(double verticalOffset)
        {
            PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
            PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
            //double h = Element.ActualHeight;
            return ((page.ActualHeight / 2) < (verticalOffset + 64d));
        }

        private Point DetermineOffset()
        {
            PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
            PhoneApplicationPage page = frame != null ? frame.Content as PhoneApplicationPage : null;
            if (page == null) throw new ArgumentNullException("PhoneApplicationPage");

            if (Control.GetVisualAncestors().Contains(page))
            {
                GeneralTransform gt = Control.TransformToVisual(page);

                Point pt;
                gt.TryTransform(new Point(), out pt);
                return pt;
            }
            else return new Point();
        }

        private void UnbindElement()
        {
            Element.ManipulationDelta -= HandleManipulationDelta;
            Element.ManipulationCompleted -= HandleManipulationCompleted;
            Element.Unloaded -= HandleUnloaded;
        }



        private void HandleManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            IsPressed = false;
            if (isOpened)
            {
                e.Handled = true;
                if (Overlay != null && !Overlay.IsOpened)
                {
                    CancelMenuOpening();
                    Overlay.IsOpen = false;
                }
                else
                {
                    Control.IsEnabled = true;
                    UnbindElement();
                }
            }
            else
            {
                CancelMenuOpening();
            }
        }

        private void HandleManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            if (!isOpened)
            {
                CancelMenuOpening();
            }
            else
            {
                e.Handled = true;
                e.Complete();
            }
        }

        /// <summary>
        /// Cancels to open the menu.
        /// </summary>
        public void CancelMenuOpening()
        {
            StopTimer();
            UnbindElement();

        }


        /// <summary>
        /// Closes the context menu.
        /// </summary>
        public void CloseContextMenu()
        {
            if (!isOpened)
            {
                CancelMenuOpening();
            }
            else
            {
                CancelMenuOpening();
                Overlay.IsOpen = false;
            }
        }

        internal static void TryOpenContextMenu(FrameworkElement element, DataTemplate template)
        {
            var cm = ContextMenuViewModel.Current;
            if (cm != null)
            {
                if (cm.Element != element)
                {
                    cm.CloseContextMenu();
                    cm = new ContextMenuViewModel(element, template);
                }
            }
            else cm = new ContextMenuViewModel(element, template);

            cm.OpenMenu();

        }
    }

}
